Using open source data from the ACLED’s website, I have created three maps of Ukraine since Russia’s full-scale invasion began on February 24.




Blank map of Ukraine

The map of Ukraine I am using includes Russian-claimed Ukrainian territory - including Crimea as well as Donetsk and Luhansk.

ukraine_map <-
  gisco_get_countries(
    country = c("Ukraine"),
    resolution = "3",
    epsg = "4326",
    year = "2016"
  )

coast <- gisco_get_coastallines(
  resolution = "3",
  epsg = "4326",
  year = "2016"
)

ggplot(coast) +
  geom_sf(color = "white") +
  geom_sf(data = ukraine_map, fill = "grey80", color = "white") +
  coord_sf(
    xlim = c(22, 41),
    ylim = c(44, 53)
  ) +
  
  theme(
    axis.ticks = element_blank(),
    axis.text = element_blank()
  ) +
  facet_wrap(vars(NAME_ENGL), ncol = 2)




Downloading the data

I downloaded the ACLED data from February 24 through April 1 for any event which occurred in Ukraine.

ukraine_acled <- read_csv("feb24_april1.csv")
## Rows: 2765 Columns: 31
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (18): event_id_cnty, event_date, event_type, sub_event_type, actor1, ass...
## dbl (13): data_id, iso, event_id_no_cnty, year, time_precision, inter1, inte...
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Then, I glimpsed the names of the columns.

glimpse(ukraine_acled)
## Rows: 2,765
## Columns: 31
## $ data_id          <dbl> 8982947, 8982957, 8982966, 8982975, 8982978, 8982981,…
## $ iso              <dbl> 804, 804, 804, 804, 804, 804, 804, 804, 804, 804, 804…
## $ event_id_cnty    <chr> "UKR54326", "UKR54357", "UKR54488", "UKR54521", "UKR5…
## $ event_id_no_cnty <dbl> 54326, 54357, 54488, 54521, 54537, 54552, 54558, 5456…
## $ event_date       <chr> "01 April 2022", "01 April 2022", "01 April 2022", "0…
## $ year             <dbl> 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022,…
## $ time_precision   <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
## $ event_type       <chr> "Explosions/Remote violence", "Battles", "Battles", "…
## $ sub_event_type   <chr> "Shelling/artillery/missile attack", "Non-state actor…
## $ actor1           <chr> "Military Forces of Russia (2000-)", "Military Forces…
## $ assoc_actor_1    <chr> NA, NA, "NAF: United Armed Forces of Novorossiya; Don…
## $ inter1           <dbl> 8, 8, 8, 1, 1, 8, 8, 8, 8, 1, 1, 1, 8, 1, 1, 1, 1, 8,…
## $ actor2           <chr> "Civilians (Ukraine)", "Military Forces of Ukraine (2…
## $ assoc_actor_2    <chr> "Health Workers (Ukraine)", NA, NA, "NAF: United Arme…
## $ inter2           <dbl> 7, 1, 1, 8, 8, 7, 1, 7, 0, 8, 8, 8, 1, 8, 8, 8, 8, 7,…
## $ interaction      <dbl> 78, 18, 18, 18, 18, 78, 18, 78, 80, 18, 18, 18, 18, 1…
## $ region           <chr> "Europe", "Europe", "Europe", "Europe", "Europe", "Eu…
## $ country          <chr> "Ukraine", "Ukraine", "Ukraine", "Ukraine", "Ukraine"…
## $ admin1           <chr> "Chernihiv", "Kharkiv", "Donetsk", "Donetsk", "Donets…
## $ admin2           <chr> "Chernihivskyi", "Iziumskyi", "Mariupolskyi", "Pokrov…
## $ admin3           <chr> "Chepnigivska", "Iziumska", "Mariupolska", "Marinska"…
## $ location         <chr> "Chernihiv", "Izium", "Mariupol", "Marinka", "Novobak…
## $ latitude         <dbl> 51.5055, 49.2088, 47.1298, 47.9425, 48.2489, 47.4029,…
## $ longitude        <dbl> 31.2849, 37.2485, 37.5710, 37.5050, 37.7875, 37.7328,…
## $ geo_precision    <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1,…
## $ source           <chr> "24 Channel; Suspilne Media", "Flot 2017; Novoye Vrem…
## $ source_scale     <chr> "National", "Local partner-New media", "Other-Nationa…
## $ notes            <chr> "On 1 April 2022, Russian forces shelled the oncology…
## $ fatalities       <dbl> 0, 0, 0, 4, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
## $ timestamp        <dbl> 1649184809, 1649184809, 1649184809, 1649184809, 16491…
## $ iso3             <chr> "UKR", "UKR", "UKR", "UKR", "UKR", "UKR", "UKR", "UKR…




Map 1: Ukraine’s protests against Russia

For the first map, I wanted to look at how many protests had been staged by Ukrainians against Russia. For all of the maps I create, I plot the points as translucent, since this creates a heat map. A more vibrant color means that there are more events built up in that area. I did not scale the points based on the size of the protest since nearly all of the protests were listed as “size unreported.”

protests_ukr <- ukraine_acled %>%
  filter(actor1 == "Protesters (Ukraine)") %>%
  mutate(clean_date = dmy(event_date)) %>%
  group_by(clean_date, location)
protests_ukr %>%
  select(clean_date, latitude, longitude, location)
protests_ukr$clean_date <- as.Date(protests_ukr$clean_date)
protests_ukr$latitude <- as.numeric(protests_ukr$latitude) 
protests_ukr$longitude <- as.numeric(protests_ukr$longitude) 

After the data frame has been created, I am creating an animated map to show the protests which occurred, day by day.

map_final <- ggplot(coast) +
  geom_sf(color = "white") +
  geom_sf(data = ukraine_map, fill = "grey80", color = "white") +
  geom_point(data = protests_ukr, aes(x = longitude, y = latitude), 
             colour = "#c74eb9", alpha = 0.5) +

  
  coord_sf(
    xlim = c(22, 41),
    ylim = c(44, 53)
  ) +
  theme(
    axis.ticks = element_blank(),
    axis.text = element_blank()
  ) +
  
  facet_wrap(vars(CNTR_NAME), ncol = 2) +
  
  labs(title = "Date: {frame_time}") +
  transition_time(clean_date) +
  ease_aes("linear")

animate(map_final)

This map shows that in early March, in the south of Ukraine around Kherson oblast, there were a lot of protests. This aligns with when Russia claimed occupation of Kherson – March 5 was the date of the large protest in Kherson, when a man with a Ukrainian flag jumped on top of a Russian tank rolling through the city center. Video on Hromadske’s Twitter here.



Then, I also want to show where the protests have occurred in Ukraine all together. Instead of showing which days were the most active, a non-animated map shows which regions are overall had the most protest activity.

 ggplot(coast) +
  geom_sf(color = "white") +
  geom_sf(data = ukraine_map, fill = "grey80", color = "white") +
  geom_point(data = protests_ukr, aes(x = longitude, y = latitude), 
             colour = "#c74eb9", alpha = 0.5) +

  
  coord_sf(
    xlim = c(22, 41),
    ylim = c(44, 53)
  ) +
  theme(
    axis.ticks = element_blank(),
    axis.text = element_blank()
  ) +
  
  facet_wrap(vars(CNTR_NAME), ncol = 2) 

The southern oblasts, particulary Kherson oblast again, are active. Kherson oblast is critical for Russia, since control of this territory would create a land bridge between Russia’s previously controlled territories: Crimea and Donetsk/Luhansk.


A current theory is that the Russian occupation is preparing to hold “referendums” in Kherson oblast, similar to those held in Donetsk and Luhansk, to create the Kherson People’s Republic.


Russia’s narrative is that these territories voluntarily aligned themselves with Russia, although the elections that took place are generally regarded as staged. This likely motivated the additional protests in Kherson oblast, to make clear that Kherson’s occupation was against its will.




Map 2: Russia’s military action

Next, I want to look at what military action Russia has taken against Ukraine.

rus_mil <- ukraine_acled %>%
  filter(str_detect(actor1, "Military Forces of Russia")) %>%
  mutate(clean_date = dmy(event_date)) %>%
  group_by(clean_date, location)


rus_mil %>%
  select(clean_date, longitude, latitude, location, event_type)

Since this data frame has so many entries to plot, I want to scale down the size of the points. I am still plotting the points as transluscent, but smaller dots will make the map look a little more precise.

ggplot(coast) +
  geom_sf(color = "white") +
  geom_sf(data = ukraine_map, fill = "grey80", color = "white") +
  geom_point(data = rus_mil, aes(x = longitude, y = latitude), 
             colour = "#b82f25", alpha = 0.20, size = 0.3) +

  coord_sf(
    xlim = c(22, 41),
    ylim = c(44, 53)
  ) +
  theme(
    axis.ticks = element_blank(),
    axis.text = element_blank()
  ) +
  
  facet_wrap(vars(CNTR_NAME), ncol = 2) 

Although Russia has been able to hit a large amount of Ukraine, including western cities like Lviv, the hottest areas are Kyiv and its suburbs, as well as what looks to be the boundary between occupied Donetsk/Luhansk and the larger oblasts.

Map of the eastern oblasts and Russia’s claims to the territory, courtesy of BBC.




Map 3: Ukrainian military actions

ukr_mil <- ukraine_acled %>%
  filter(str_detect(actor1, "Military Forces of Ukraine")) %>%
  mutate(clean_date = dmy(event_date)) %>%
  group_by(clean_date, location)


ukr_mil %>%
  select(clean_date, longitude, latitude, location, event_type)
ggplot(coast) +
  geom_sf(color = "white") +
  geom_sf(data = ukraine_map, fill = "grey80", color = "white") +
  geom_point(data = ukr_mil, aes(x = longitude, y = latitude), 
             colour = "#4173e8", alpha = 0.20, size = 0.3) +

  coord_sf(
    xlim = c(22, 41),
    ylim = c(44, 53)
  ) +
  theme(
    axis.ticks = element_blank(),
    axis.text = element_blank()
  ) +
  
  facet_wrap(vars(CNTR_NAME), ncol = 2) 

ukr_mil_map <- ggplot(coast) +
  geom_sf(color = "white") +
  geom_sf(data = ukraine_map, fill = "grey80", color = "white") +
  geom_point(data = ukr_mil, aes(x = longitude, y = latitude), 
             colour = "#4173e8", alpha = 0.2, size = 0.3) +

  
  coord_sf(
    xlim = c(22, 41),
    ylim = c(44, 53)
  ) +
  theme(
    axis.ticks = element_blank(),
    axis.text = element_blank()
  ) +
  
  facet_wrap(vars(CNTR_NAME), ncol = 2) +
  
  labs(title = "Date: {frame_time}") +
  transition_time(clean_date) +
  ease_aes("linear")

animate(ukr_mil_map)

These maps both show concentrated activity around Donetsk/Luhansk and Kyiv as well. One aspect to note in the animated map is the increase of activity at the end of March around the Kyiv area, since this is when Russia withdrew from Kyiv and the suburbs around the city began to be liberated by the Armed Forces of Ukraine.

LS0tCnRpdGxlOiAidGhyZWUgbWFwcyBvZiBVa3JhaW5lIHNpbmNlIHRoZSBmdWxsLXNjYWxlIGludmFzaW9uIgphdXRob3I6ICJiIgpkYXRlOiAiNC8yMS8yMDIyIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICAgIHRoZW1lOiBjZXJ1bGVhbgogICAgICB0b2M6IHRydWUKICAgICAgdG9jX2Zsb2F0OiB0cnVlCiAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgICAgZGZfcHJpbnQ6IHBhZ2VkCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIGVycm9yID0gVFJVRSkKCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGphbml0b3IpCmxpYnJhcnkobHVicmlkYXRlKQoKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHNmKQpsaWJyYXJ5KHRpZ3JpcykKbGlicmFyeSh0bWFwKQpsaWJyYXJ5KGd0KQoKbGlicmFyeShnZ3RoZW1lcykKbGlicmFyeShnZ2FuaW1hdGUpCmxpYnJhcnkobWFwcykKbGlicmFyeShkcGx5cikKbGlicmFyeShnaXNjb1IpCgoKZ2lzY29fYXR0cmlidXRpb25zKGNvcHlyaWdodCA9IFRSVUUpCgoKCm9wdGlvbnModGlncmlzX2NsYXNzPSJzZiIsCiAgICAgICAgdGlncmlzX3VzZV9jYWNoZSA9IFRSVUUpCgoKYGBgCgojIyMjIFVzaW5nIG9wZW4gc291cmNlIGRhdGEgZnJvbSB0aGUgQUNMRUQncyB3ZWJzaXRlLCBJIGhhdmUgY3JlYXRlZCB0aHJlZSBtYXBzIG9mIFVrcmFpbmUgc2luY2UgUnVzc2lhJ3MgZnVsbC1zY2FsZSBpbnZhc2lvbiBiZWdhbiBvbiBGZWJydWFyeSAyNC4gCgo8YnI+IAo8YnI+IAo8YnI+IAoKIyMjIEJsYW5rIG1hcCBvZiBVa3JhaW5lIAojIyMjIFRoZSBtYXAgb2YgVWtyYWluZSBJIGFtIHVzaW5nIGluY2x1ZGVzIFJ1c3NpYW4tY2xhaW1lZCBVa3JhaW5pYW4gdGVycml0b3J5IC0gaW5jbHVkaW5nIENyaW1lYSBhcyB3ZWxsIGFzIERvbmV0c2sgYW5kIEx1aGFuc2suIAoKCmBgYHtyfQoKdWtyYWluZV9tYXAgPC0KICBnaXNjb19nZXRfY291bnRyaWVzKAogICAgY291bnRyeSA9IGMoIlVrcmFpbmUiKSwKICAgIHJlc29sdXRpb24gPSAiMyIsCiAgICBlcHNnID0gIjQzMjYiLAogICAgeWVhciA9ICIyMDE2IgogICkKCmNvYXN0IDwtIGdpc2NvX2dldF9jb2FzdGFsbGluZXMoCiAgcmVzb2x1dGlvbiA9ICIzIiwKICBlcHNnID0gIjQzMjYiLAogIHllYXIgPSAiMjAxNiIKKQoKZ2dwbG90KGNvYXN0KSArCiAgZ2VvbV9zZihjb2xvciA9ICJ3aGl0ZSIpICsKICBnZW9tX3NmKGRhdGEgPSB1a3JhaW5lX21hcCwgZmlsbCA9ICJncmV5ODAiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBjb29yZF9zZigKICAgIHhsaW0gPSBjKDIyLCA0MSksCiAgICB5bGltID0gYyg0NCwgNTMpCiAgKSArCiAgCiAgdGhlbWUoCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpCiAgKSArCiAgZmFjZXRfd3JhcCh2YXJzKE5BTUVfRU5HTCksIG5jb2wgPSAyKQoKCmBgYAoKPGJyPiAKPGJyPiAKPGJyPiAKCiMjIyBEb3dubG9hZGluZyB0aGUgZGF0YQojIyMjIEkgZG93bmxvYWRlZCB0aGUgQUNMRUQgZGF0YSBmcm9tIEZlYnJ1YXJ5IDI0IHRocm91Z2ggQXByaWwgMSBmb3IgYW55IGV2ZW50IHdoaWNoIG9jY3VycmVkIGluIFVrcmFpbmUuIAoKYGBge3J9Cgp1a3JhaW5lX2FjbGVkIDwtIHJlYWRfY3N2KCJmZWIyNF9hcHJpbDEuY3N2IikKCmBgYAoKIyMjIyBUaGVuLCBJIGdsaW1wc2VkIHRoZSBuYW1lcyBvZiB0aGUgY29sdW1ucy4gCgpgYGB7cn0KCmdsaW1wc2UodWtyYWluZV9hY2xlZCkKCgpgYGAKCjxicj4gCjxicj4gCjxicj4gCgojIyMgTWFwIDE6IFVrcmFpbmUncyBwcm90ZXN0cyBhZ2FpbnN0IFJ1c3NpYQojIyMjIEZvciB0aGUgZmlyc3QgbWFwLCBJIHdhbnRlZCB0byBsb29rIGF0IGhvdyBtYW55IHByb3Rlc3RzIGhhZCBiZWVuIHN0YWdlZCBieSBVa3JhaW5pYW5zIGFnYWluc3QgUnVzc2lhLiBGb3IgYWxsIG9mIHRoZSBtYXBzIEkgY3JlYXRlLCBJIHBsb3QgdGhlIHBvaW50cyBhcyB0cmFuc2x1Y2VudCwgc2luY2UgdGhpcyBjcmVhdGVzIGEgaGVhdCBtYXAuIEEgbW9yZSB2aWJyYW50IGNvbG9yIG1lYW5zIHRoYXQgdGhlcmUgYXJlIG1vcmUgZXZlbnRzIGJ1aWx0IHVwIGluIHRoYXQgYXJlYS4gSSBkaWQgbm90IHNjYWxlIHRoZSBwb2ludHMgYmFzZWQgb24gdGhlIHNpemUgb2YgdGhlIHByb3Rlc3Qgc2luY2UgbmVhcmx5IGFsbCBvZiB0aGUgcHJvdGVzdHMgd2VyZSBsaXN0ZWQgYXMgInNpemUgdW5yZXBvcnRlZC4iIAoKYGBge3J9Cgpwcm90ZXN0c191a3IgPC0gdWtyYWluZV9hY2xlZCAlPiUKICBmaWx0ZXIoYWN0b3IxID09ICJQcm90ZXN0ZXJzIChVa3JhaW5lKSIpICU+JQogIG11dGF0ZShjbGVhbl9kYXRlID0gZG15KGV2ZW50X2RhdGUpKSAlPiUKICBncm91cF9ieShjbGVhbl9kYXRlLCBsb2NhdGlvbikKCmBgYAoKYGBge3J9Cgpwcm90ZXN0c191a3IgJT4lCiAgc2VsZWN0KGNsZWFuX2RhdGUsIGxhdGl0dWRlLCBsb25naXR1ZGUsIGxvY2F0aW9uKQoKcHJvdGVzdHNfdWtyJGNsZWFuX2RhdGUgPC0gYXMuRGF0ZShwcm90ZXN0c191a3IkY2xlYW5fZGF0ZSkKcHJvdGVzdHNfdWtyJGxhdGl0dWRlIDwtIGFzLm51bWVyaWMocHJvdGVzdHNfdWtyJGxhdGl0dWRlKSAKcHJvdGVzdHNfdWtyJGxvbmdpdHVkZSA8LSBhcy5udW1lcmljKHByb3Rlc3RzX3VrciRsb25naXR1ZGUpIAoKCmBgYAoKIyMjIyBBZnRlciB0aGUgZGF0YSBmcmFtZSBoYXMgYmVlbiBjcmVhdGVkLCBJIGFtIGNyZWF0aW5nIGFuIGFuaW1hdGVkIG1hcCB0byBzaG93IHRoZSBwcm90ZXN0cyB3aGljaCBvY2N1cnJlZCwgZGF5IGJ5IGRheS4gCgpgYGB7cn0KCm1hcF9maW5hbCA8LSBnZ3Bsb3QoY29hc3QpICsKICBnZW9tX3NmKGNvbG9yID0gIndoaXRlIikgKwogIGdlb21fc2YoZGF0YSA9IHVrcmFpbmVfbWFwLCBmaWxsID0gImdyZXk4MCIsIGNvbG9yID0gIndoaXRlIikgKwogIGdlb21fcG9pbnQoZGF0YSA9IHByb3Rlc3RzX3VrciwgYWVzKHggPSBsb25naXR1ZGUsIHkgPSBsYXRpdHVkZSksIAogICAgICAgICAgICAgY29sb3VyID0gIiNjNzRlYjkiLCBhbHBoYSA9IDAuNSkgKwoKICAKICBjb29yZF9zZigKICAgIHhsaW0gPSBjKDIyLCA0MSksCiAgICB5bGltID0gYyg0NCwgNTMpCiAgKSArCiAgdGhlbWUoCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpCiAgKSArCiAgCiAgZmFjZXRfd3JhcCh2YXJzKENOVFJfTkFNRSksIG5jb2wgPSAyKSArCiAgCiAgbGFicyh0aXRsZSA9ICJEYXRlOiB7ZnJhbWVfdGltZX0iKSArCiAgdHJhbnNpdGlvbl90aW1lKGNsZWFuX2RhdGUpICsKICBlYXNlX2FlcygibGluZWFyIikKCmFuaW1hdGUobWFwX2ZpbmFsKQoKYGBgCgojIyMjIFRoaXMgbWFwIHNob3dzIHRoYXQgaW4gZWFybHkgTWFyY2gsIGluIHRoZSBzb3V0aCBvZiBVa3JhaW5lIGFyb3VuZCBLaGVyc29uIG9ibGFzdCwgdGhlcmUgd2VyZSBhIGxvdCBvZiBwcm90ZXN0cy4gVGhpcyBhbGlnbnMgd2l0aCB3aGVuIFJ1c3NpYSBjbGFpbWVkIG9jY3VwYXRpb24gb2YgS2hlcnNvbiAtLSBNYXJjaCA1IHdhcyB0aGUgZGF0ZSBvZiB0aGUgbGFyZ2UgcHJvdGVzdCBpbiBLaGVyc29uLCB3aGVuIGEgbWFuIHdpdGggYSBVa3JhaW5pYW4gZmxhZyBqdW1wZWQgb24gdG9wIG9mIGEgUnVzc2lhbiB0YW5rIHJvbGxpbmcgdGhyb3VnaCB0aGUgY2l0eSBjZW50ZXIuIFZpZGVvIG9uIEhyb21hZHNrZSdzIFR3aXR0ZXIgW2hlcmVdKGh0dHBzOi8vdHdpdHRlci5jb20vSHJvbWFkc2tlUmFkaW8vc3RhdHVzLzE1MDAwNTA3MDUxMDg5MzQ2NTg/cz0yMCZ0PXBLeVYtU2NVdHdJejlCMWN4SGJpT2cpLiAKCjxibG9ja3F1b3RlIGNsYXNzPSJ0d2l0dGVyLXR3ZWV0Ij48cCBsYW5nPSJ1bmQiIGRpcj0ibHRyIj7QkdC10LfRgdGC0YDQsNGI0L3RliDRhdC10YDRgdC+0L3RhtGWITxicj48YnI+0J7QtNC40L0g0Lcg0YPRh9Cw0YHQvdC40LrRltCyINCw0LrRhtGW0ZcgwqvQpdC10YDRgdC+0L0g4oCUINGG0LUg0KPQutGA0LDRl9C90LDCuyDQstC40LvRltC3INC3INGD0LrRgNCw0ZfQvdGB0YzQutC40Lwg0YHRgtGP0LPQvtC8INC/0YDRj9C80L4g0L3QsCDRgtC10YXQvdGW0LrRgyDRgNC+0YHRltC50YHRjNC60LjRhSDQvtC60YPQv9Cw0L3RgtGW0LIhIPCfh7rwn4emPGJyPjxicj5GZWFybGVzcyBLaGVyc29uIHJlc2lkZW50cyE8YnI+PGJyPkEgbWFuIGNsaW1iZWQgd2l0aCB0aGUgVWtyYWluaWFuIGZsYWcgZGlyZWN0bHkgb24gdGhlIG1hY2hpbmVyeSBvZiB0aGUgUnVzc2lhbiBvY2N1cGllcnMhIDxhIGhyZWY9Imh0dHBzOi8vdC5jby9lYTZpWHBhVEliIj5waWMudHdpdHRlci5jb20vZWE2aVhwYVRJYjwvYT48L3A+Jm1kYXNoOyDQk9GA0L7QvNCw0LTRgdGM0LrQtSDRgNCw0LTRltC+IChASHJvbWFkc2tlUmFkaW8pIDxhIGhyZWY9Imh0dHBzOi8vdHdpdHRlci5jb20vSHJvbWFkc2tlUmFkaW8vc3RhdHVzLzE1MDAwNTA3MDUxMDg5MzQ2NTg/cmVmX3NyYz10d3NyYyU1RXRmdyI+TWFyY2ggNSwgMjAyMjwvYT48L2Jsb2NrcXVvdGU+IDxzY3JpcHQgYXN5bmMgc3JjPSJodHRwczovL3BsYXRmb3JtLnR3aXR0ZXIuY29tL3dpZGdldHMuanMiIGNoYXJzZXQ9InV0Zi04Ij48L3NjcmlwdD4KCiFbXShraGVyc29uIHByb3Rlc3QgNSBtYXJjaC5wbmcpCgo8YnI+Cjxicj4KCiMjIyMgVGhlbiwgSSBhbHNvIHdhbnQgdG8gc2hvdyB3aGVyZSB0aGUgcHJvdGVzdHMgaGF2ZSBvY2N1cnJlZCBpbiBVa3JhaW5lIGFsbCB0b2dldGhlci4gSW5zdGVhZCBvZiBzaG93aW5nIHdoaWNoIGRheXMgd2VyZSB0aGUgbW9zdCBhY3RpdmUsIGEgbm9uLWFuaW1hdGVkIG1hcCBzaG93cyB3aGljaCByZWdpb25zIGFyZSBvdmVyYWxsIGhhZCB0aGUgbW9zdCBwcm90ZXN0IGFjdGl2aXR5LiAKCgpgYGB7cn0KIGdncGxvdChjb2FzdCkgKwogIGdlb21fc2YoY29sb3IgPSAid2hpdGUiKSArCiAgZ2VvbV9zZihkYXRhID0gdWtyYWluZV9tYXAsIGZpbGwgPSAiZ3JleTgwIiwgY29sb3IgPSAid2hpdGUiKSArCiAgZ2VvbV9wb2ludChkYXRhID0gcHJvdGVzdHNfdWtyLCBhZXMoeCA9IGxvbmdpdHVkZSwgeSA9IGxhdGl0dWRlKSwgCiAgICAgICAgICAgICBjb2xvdXIgPSAiI2M3NGViOSIsIGFscGhhID0gMC41KSArCgogIAogIGNvb3JkX3NmKAogICAgeGxpbSA9IGMoMjIsIDQxKSwKICAgIHlsaW0gPSBjKDQ0LCA1MykKICApICsKICB0aGVtZSgKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCkKICApICsKICAKICBmYWNldF93cmFwKHZhcnMoQ05UUl9OQU1FKSwgbmNvbCA9IDIpIAogIAoKYGBgCgojIyMjIFRoZSBzb3V0aGVybiBvYmxhc3RzLCBwYXJ0aWN1bGFyeSBLaGVyc29uIG9ibGFzdCBhZ2FpbiwgYXJlIGFjdGl2ZS4gS2hlcnNvbiBvYmxhc3QgaXMgY3JpdGljYWwgZm9yIFJ1c3NpYSwgc2luY2UgY29udHJvbCBvZiB0aGlzIHRlcnJpdG9yeSB3b3VsZCBjcmVhdGUgYSBsYW5kIGJyaWRnZSBiZXR3ZWVuIFJ1c3NpYSdzIHByZXZpb3VzbHkgY29udHJvbGxlZCB0ZXJyaXRvcmllczogQ3JpbWVhIGFuZCBEb25ldHNrL0x1aGFuc2suIAoKPGJyPgoKIyMjIyBBIGN1cnJlbnQgdGhlb3J5IGlzIHRoYXQgdGhlIFJ1c3NpYW4gb2NjdXBhdGlvbiBpcyBwcmVwYXJpbmcgdG8gaG9sZCAicmVmZXJlbmR1bXMiIGluIEtoZXJzb24gb2JsYXN0LCBzaW1pbGFyIHRvIHRob3NlIGhlbGQgaW4gRG9uZXRzayBhbmQgTHVoYW5zaywgdG8gY3JlYXRlIHRoZSBLaGVyc29uIFBlb3BsZSdzIFJlcHVibGljLiAKCjxicj4KIAojIyMjIFJ1c3NpYSdzIG5hcnJhdGl2ZSBpcyB0aGF0IHRoZXNlIHRlcnJpdG9yaWVzIHZvbHVudGFyaWx5IGFsaWduZWQgdGhlbXNlbHZlcyB3aXRoIFJ1c3NpYSwgYWx0aG91Z2ggdGhlIGVsZWN0aW9ucyB0aGF0IHRvb2sgcGxhY2UgYXJlIGdlbmVyYWxseSByZWdhcmRlZCBhcyBzdGFnZWQuIFRoaXMgbGlrZWx5IG1vdGl2YXRlZCB0aGUgYWRkaXRpb25hbCBwcm90ZXN0cyBpbiBLaGVyc29uIG9ibGFzdCwgdG8gbWFrZSBjbGVhciB0aGF0IEtoZXJzb24ncyBvY2N1cGF0aW9uIHdhcyBhZ2FpbnN0IGl0cyB3aWxsLiAKCjxicj4KPGJyPgo8YnI+CgojIyMgTWFwIDI6IFJ1c3NpYSdzIG1pbGl0YXJ5IGFjdGlvbiAKIyMjIyBOZXh0LCBJIHdhbnQgdG8gbG9vayBhdCB3aGF0IG1pbGl0YXJ5IGFjdGlvbiBSdXNzaWEgaGFzIHRha2VuIGFnYWluc3QgVWtyYWluZS4gCgpgYGB7cn0KCnJ1c19taWwgPC0gdWtyYWluZV9hY2xlZCAlPiUKICBmaWx0ZXIoc3RyX2RldGVjdChhY3RvcjEsICJNaWxpdGFyeSBGb3JjZXMgb2YgUnVzc2lhIikpICU+JQogIG11dGF0ZShjbGVhbl9kYXRlID0gZG15KGV2ZW50X2RhdGUpKSAlPiUKICBncm91cF9ieShjbGVhbl9kYXRlLCBsb2NhdGlvbikKCgpydXNfbWlsICU+JQogIHNlbGVjdChjbGVhbl9kYXRlLCBsb25naXR1ZGUsIGxhdGl0dWRlLCBsb2NhdGlvbiwgZXZlbnRfdHlwZSkKCgoKYGBgCiMjIyMgU2luY2UgdGhpcyBkYXRhIGZyYW1lIGhhcyBzbyBtYW55IGVudHJpZXMgdG8gcGxvdCwgSSB3YW50IHRvIHNjYWxlIGRvd24gdGhlIHNpemUgb2YgdGhlIHBvaW50cy4gSSBhbSBzdGlsbCBwbG90dGluZyB0aGUgcG9pbnRzIGFzIHRyYW5zbHVzY2VudCwgYnV0IHNtYWxsZXIgZG90cyB3aWxsIG1ha2UgdGhlIG1hcCBsb29rIGEgbGl0dGxlIG1vcmUgcHJlY2lzZS4gCgpgYGB7cn0KCmdncGxvdChjb2FzdCkgKwogIGdlb21fc2YoY29sb3IgPSAid2hpdGUiKSArCiAgZ2VvbV9zZihkYXRhID0gdWtyYWluZV9tYXAsIGZpbGwgPSAiZ3JleTgwIiwgY29sb3IgPSAid2hpdGUiKSArCiAgZ2VvbV9wb2ludChkYXRhID0gcnVzX21pbCwgYWVzKHggPSBsb25naXR1ZGUsIHkgPSBsYXRpdHVkZSksIAogICAgICAgICAgICAgY29sb3VyID0gIiNiODJmMjUiLCBhbHBoYSA9IDAuMjAsIHNpemUgPSAwLjMpICsKCiAgY29vcmRfc2YoCiAgICB4bGltID0gYygyMiwgNDEpLAogICAgeWxpbSA9IGMoNDQsIDUzKQogICkgKwogIHRoZW1lKAogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKQogICkgKwogIAogIGZhY2V0X3dyYXAodmFycyhDTlRSX05BTUUpLCBuY29sID0gMikgCiAgCgpgYGAKCiMjIyMgQWx0aG91Z2ggUnVzc2lhIGhhcyBiZWVuIGFibGUgdG8gaGl0IGEgbGFyZ2UgYW1vdW50IG9mIFVrcmFpbmUsIGluY2x1ZGluZyB3ZXN0ZXJuIGNpdGllcyBsaWtlIEx2aXYsIHRoZSBob3R0ZXN0IGFyZWFzIGFyZSBLeWl2IGFuZCBpdHMgc3VidXJicywgYXMgd2VsbCBhcyB3aGF0IGxvb2tzIHRvIGJlIHRoZSBib3VuZGFyeSBiZXR3ZWVuIG9jY3VwaWVkIERvbmV0c2svTHVoYW5zayBhbmQgdGhlIGxhcmdlciBvYmxhc3RzLiAKCiFbXShiYmMgbWFwIGRvbmJhcy5qcGcpCgojIyMjIE1hcCBvZiB0aGUgZWFzdGVybiBvYmxhc3RzIGFuZCBSdXNzaWEncyBjbGFpbXMgdG8gdGhlIHRlcnJpdG9yeSwgY291cnRlc3kgb2YgQkJDLiAKCjxicj4KPGJyPgo8YnI+CgojIyMgTWFwIDM6IFVrcmFpbmlhbiBtaWxpdGFyeSBhY3Rpb25zCgpgYGB7cn0KCnVrcl9taWwgPC0gdWtyYWluZV9hY2xlZCAlPiUKICBmaWx0ZXIoc3RyX2RldGVjdChhY3RvcjEsICJNaWxpdGFyeSBGb3JjZXMgb2YgVWtyYWluZSIpKSAlPiUKICBtdXRhdGUoY2xlYW5fZGF0ZSA9IGRteShldmVudF9kYXRlKSkgJT4lCiAgZ3JvdXBfYnkoY2xlYW5fZGF0ZSwgbG9jYXRpb24pCgoKdWtyX21pbCAlPiUKICBzZWxlY3QoY2xlYW5fZGF0ZSwgbG9uZ2l0dWRlLCBsYXRpdHVkZSwgbG9jYXRpb24sIGV2ZW50X3R5cGUpCgoKCmBgYAoKYGBge3J9CgpnZ3Bsb3QoY29hc3QpICsKICBnZW9tX3NmKGNvbG9yID0gIndoaXRlIikgKwogIGdlb21fc2YoZGF0YSA9IHVrcmFpbmVfbWFwLCBmaWxsID0gImdyZXk4MCIsIGNvbG9yID0gIndoaXRlIikgKwogIGdlb21fcG9pbnQoZGF0YSA9IHVrcl9taWwsIGFlcyh4ID0gbG9uZ2l0dWRlLCB5ID0gbGF0aXR1ZGUpLCAKICAgICAgICAgICAgIGNvbG91ciA9ICIjNDE3M2U4IiwgYWxwaGEgPSAwLjIwLCBzaXplID0gMC4zKSArCgogIGNvb3JkX3NmKAogICAgeGxpbSA9IGMoMjIsIDQxKSwKICAgIHlsaW0gPSBjKDQ0LCA1MykKICApICsKICB0aGVtZSgKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCkKICApICsKICAKICBmYWNldF93cmFwKHZhcnMoQ05UUl9OQU1FKSwgbmNvbCA9IDIpIAogIAoKYGBgCgoKCmBgYHtyfQoKdWtyX21pbF9tYXAgPC0gZ2dwbG90KGNvYXN0KSArCiAgZ2VvbV9zZihjb2xvciA9ICJ3aGl0ZSIpICsKICBnZW9tX3NmKGRhdGEgPSB1a3JhaW5lX21hcCwgZmlsbCA9ICJncmV5ODAiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBnZW9tX3BvaW50KGRhdGEgPSB1a3JfbWlsLCBhZXMoeCA9IGxvbmdpdHVkZSwgeSA9IGxhdGl0dWRlKSwgCiAgICAgICAgICAgICBjb2xvdXIgPSAiIzQxNzNlOCIsIGFscGhhID0gMC4yLCBzaXplID0gMC4zKSArCgogIAogIGNvb3JkX3NmKAogICAgeGxpbSA9IGMoMjIsIDQxKSwKICAgIHlsaW0gPSBjKDQ0LCA1MykKICApICsKICB0aGVtZSgKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCkKICApICsKICAKICBmYWNldF93cmFwKHZhcnMoQ05UUl9OQU1FKSwgbmNvbCA9IDIpICsKICAKICBsYWJzKHRpdGxlID0gIkRhdGU6IHtmcmFtZV90aW1lfSIpICsKICB0cmFuc2l0aW9uX3RpbWUoY2xlYW5fZGF0ZSkgKwogIGVhc2VfYWVzKCJsaW5lYXIiKQoKYW5pbWF0ZSh1a3JfbWlsX21hcCkKCmBgYAoKIyMjIyBUaGVzZSBtYXBzIGJvdGggc2hvdyBjb25jZW50cmF0ZWQgYWN0aXZpdHkgYXJvdW5kIERvbmV0c2svTHVoYW5zayBhbmQgS3lpdiBhcyB3ZWxsLiBPbmUgYXNwZWN0IHRvIG5vdGUgaW4gdGhlIGFuaW1hdGVkIG1hcCBpcyB0aGUgaW5jcmVhc2Ugb2YgYWN0aXZpdHkgYXQgdGhlIGVuZCBvZiBNYXJjaCBhcm91bmQgdGhlIEt5aXYgYXJlYSwgc2luY2UgdGhpcyBpcyB3aGVuIFJ1c3NpYSB3aXRoZHJldyBmcm9tIEt5aXYgYW5kIHRoZSBzdWJ1cmJzIGFyb3VuZCB0aGUgY2l0eSBiZWdhbiB0byBiZSBsaWJlcmF0ZWQgYnkgdGhlIEFybWVkIEZvcmNlcyBvZiBVa3JhaW5lLiAKCg==